package com.novax.ex.ai.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

/**
 * 背景：在进行服务间调用的时候，可能因为偶发情况导致，调用失败
 * 作用：使用AOP的环绕通知，当偶发情况出现的时候进行重试，如果程序正常执行，不会触发。
 * 注意：最大重试此数，要根据具体的业务需求，不要设置的很大。
 */
@Slf4j
@Aspect
@Component
public class RetryOnFailureHandler {

    @AfterThrowing(value = "@annotation(retryOnFailure)", throwing = "exception")
    public void retry(JoinPoint joinPoint, RetryOnFailure retryOnFailure, Exception exception) throws Exception {

        // 获取最大重试次数
        int maxRetries = retryOnFailure.maximum();

        int attempt = 1;
        while (attempt <= maxRetries) {
            try {
                // 日志打印第几次重试，重试的是什么方法
                log.warn("Retrying method: " + joinPoint.getSignature().getName() + ", attempt " + attempt);
                // 获取方法的名称
                String methodName = joinPoint.getSignature().getName();
                // 获取方法的参数列表
                Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
                // 通过方法的名称和参数列表能确定是哪个方法（因为java的重载可能出现方法名相同的情况）
                // 确定方法后通过joinPoint获取方法参数，将参数传递到被执行的方法
                joinPoint.getTarget().getClass().getMethod(methodName, parameterTypes).invoke(joinPoint.getTarget(), joinPoint.getArgs());
                return;
            } catch (Exception e) {
                if (attempt == maxRetries) {
                    System.out.println("Maximum retry attempts reached.");
                    // 达到最大重试次数
                    throw exception;
                }
                attempt++;
            }
        }
    }
}
